import urllib, urllib2
import imghdr, md5, base64, mimetypes, re, StringIO
import PIL.Image

GRAFFITI_FULL_SIZE_W = 586
GRAFFITI_THUMB_SIZE_W = 272
GRAFFITI_THUMB_SIZE_H = 136

#generic grafiti error
class GraffitiError(Exception):
    pass

#unsupported image type
class WrongImageTypeError(GraffitiError):
    pass

#wrong friendID or groupID in PostImage
class WrongPostID(GraffitiError):
    pass

def encode_multipart_formdata(fields, files):
    """
    fields is a sequence of (name, value) elements for regular form fields.
    files is a sequence of (name, filename, value) elements for data to be uploaded as files
    Return (content_type, body) ready for httplib.HTTP instance
    """
    
    def get_content_type(filename):
        return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
    
    BOUNDARY = '--OLEG-ANDREEV-PAVEL-DUROV-GRAFFITI-POST'
    CRLF = '\r\n'
    L = []
    for (key, value) in fields:
        L.append('--' + BOUNDARY)
        L.append('Content-Disposition: form-data; name="%s"' % key)
        L.append('')
        L.append(value)
    for (key, filename, value) in files:
        L.append('--' + BOUNDARY)
        L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
        L.append('Content-Type: %s' % get_content_type(filename))
        L.append('')
        L.append(value)
    L.append('--' + BOUNDARY + '--')
    L.append('')
    body = CRLF.join(L)
    content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
    return content_type, body


class Graffiti:
    def __init__(self, vkontakte_auth):
        self._auth = vkontakte_auth
        self._image_path = ''
        self._image_data = ''
        self._image_type = 'unknown'
        self._image_hash = ''
        
        self.ALLOWED_TYPES = ['jpeg', 'bmp', 'png']
        
    def LoadImage(self, local_path):        
        f = open(local_path, "rb")        
        img_type = imghdr.what(None, f.read(32))
        if img_type not in self.ALLOWED_TYPES:
            raise WrongImageTypeError()
        
        f.seek(0)
        image = PIL.Image.open(f)
        image.load()
        
        #scale it for full view (in any case)
        (w, h) = image.size        
        new_h = int(GRAFFITI_FULL_SIZE_W * h / w)
        if GRAFFITI_FULL_SIZE_W < w:
            self._full_image = image.resize((GRAFFITI_FULL_SIZE_W, new_h), PIL.Image.ANTIALIAS)
        else:
            self._full_image = image.resize((GRAFFITI_FULL_SIZE_W, new_h), PIL.Image.BICUBIC)        
        stream = StringIO.StringIO()
        self._full_image.save(stream, "JPEG")
        self._full_image_data = stream.getvalue()        
        
        #scale it for wall size (in any case)
        if GRAFFITI_THUMB_SIZE_W < w:
            self._wall_image = image.resize((GRAFFITI_THUMB_SIZE_W, GRAFFITI_THUMB_SIZE_H), PIL.Image.ANTIALIAS)
        else:
            self._wall_image = image.resize((GRAFFITI_THUMB_SIZE_W, GRAFFITI_THUMB_SIZE_H), PIL.Image.BICUBIC)
        stream = StringIO.StringIO()
        self._wall_image.save(stream, "JPEG")
        self._wall_image_data = stream.getvalue()
        
        self._image_path = local_path
        self._image_type = img_type    #original image type
        self._image_hash = md5.md5(base64.encodestring(self._full_image_data).replace('\n','')[:1024]).hexdigest()
        f.close()
        
    def GetImageData(self):
        return self._full_image_data
    
    def GetWallImageData(self):
        return self._wall_image_data
    
    def GetImageType(self):
        return self._image_type
    
    def GetImagePath(self):
        return self._image_path
    
    def IsReady(self):
        return self._image_type != 'unknown'
        
    #uploads an image and return URL of the page, to which it's uploaded
    #type is 'friend' or 'club'
    def PostImage(self, type, id):
        if type not in ['friend', 'group']:
            raise WrongPostID()
                
        opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._auth.get_cookie()))
        
        to_id = 0;
        to_group = 0;
        
        if type == 'friend':
            to_id = int(id)
            
        if type == 'group':
            to_group = int(id)
           
        post_fields = [('Signature', self._image_hash), ('Upload', 'Submit Query')]
        post_files = [('Filedata', 'graffiti.png', self._full_image_data)]
        (content_type, post_body) = encode_multipart_formdata(post_fields, post_files)
        
        query = "http://vkontakte.ru/graffiti.php?to_id=%d&group_id=%d" % (to_id, to_group)
        request = urllib2.Request(query)
        request.add_header('Referer', 'http://vkontakte.ru/swf/Graffiti.swf?12')
        request.add_header('Content-Type', content_type)
        request.add_data(post_body)
                
        response = opener.open(request)
        if not re.search(r'vkontakte\.ru/wall\.php\?act=draw&grid=\d+', response.geturl()):
            raise GraffitiError()
        
        #ok, uploaded successful, now let's confirm
        response_data = response.read()        
        confirm_fields = re.findall(r'<input type="hidden" id=".+" name="(.+)" value="(.+)" />', response_data)        
        response = opener.open("http://vkontakte.ru/wall.php", urllib.urlencode(confirm_fields))    
        
        return response.geturl()
        